﻿using System;

using BizElements.Core;

namespace BizElements.BusinessLayer
{
    /// <summary>Safe, delegate base implementation of generic interface for managing complex properties, ie. child/extension entities linked to parent entity via 1-to-1 relations. Primary key of such child is also a foreign key to parent entity.</summary>
    public sealed class ComplexPropertyAdapter<TParent, TChild> : IComplexPropertyAdapter
        where TParent : IBusinessObject
        where TChild : IDataObject, new()
    {
        Delegates.Function<TChild, TParent> getChild;
        Delegates.Procedure<TParent> fetchChild;

        /// <summary>Initializes a new instance of <b>ComplexPropertyAdapter</b> class.</summary>
        /// <param name="propertyName">Complex property name.</param>
        /// <param name="getChild">Method or lambda expression which gets property value, ie. child entity.</param>
        /// <param name="fetchChild">Method or lambda expression which fetches child entity from the data source and populates property value.</param>
        public ComplexPropertyAdapter(string propertyName, Delegates.Function<TChild, TParent> getChild, Delegates.Procedure<TParent> fetchChild)
        {
            this.PropertyName = propertyName;
            this.getChild = getChild;
            this.fetchChild = fetchChild;
        }

        /// <summary>Complex property name.</summary>
        public string PropertyName { get; private set; }

        #region IComplexPropertyAdapter methods.

        /// <summary>Gets property value, ie. child entity.</summary>
        /// <param name="parentEntity">Parent object that owns the child entity property worked by current adapter.</param>
        /// <returns>Child entity or <b>null</b> if it doesn't exist or hasn't been fetched.</returns>
        public TChild GetChild(TParent parentEntity)
        {
            return getChild(parentEntity);
        }

        /// <summary>Fetches child entity if it exists.</summary>
        /// <param name="parentEntity">Parent object that owns the child entity property worked by current adapter.</param>
        public void FetchChild(TParent parentEntity)
        {
            fetchChild(parentEntity);
        }

        /// <summary>Gets property value, ie. child entity.</summary>
        /// <param name="parentEntity">Parent object that owns the child entity property worked by current adapter.</param>
        /// <returns>Child entity or <b>null</b> if it doesn't exist or hasn't been fetched.</returns>
        public IDataObject GetChild(IBusinessObject parentEntity)
        {
            return getChild((TParent)parentEntity);
        }

        /// <summary>Fetches child entity if it exists.</summary>
        /// <param name="parentEntity">Parent object that owns the child entity property worked by current adapter.</param>
        public void FetchChild(IBusinessObject parentEntity)
        {
            fetchChild((TParent)parentEntity);
        }

        #endregion

        /// <summary>Creates a new adapter that is a copy of the current instance.</summary>
        /// <returns>A new object that is a copy of the current instance.</returns>
        public IComplexPropertyAdapter Clone()
        {
            var clone = new ComplexPropertyAdapter<TParent, TChild>(
                this.PropertyName,
                this.getChild,
                this.fetchChild);

            return clone;
        }
    }
}
